1
我的掘金:https://juejin.im/post/5c988b...

1. 前言

Hooks是React16.8版本中的新特性,它可以在不使用class声明的组件中使用state和React特性。

Tip: React v16.8.0已经支持Hooks。当我们进行更新时,别忘了更新其他相关依赖包,包括React DOM等。React Native将会在下一个稳定版本支持Hooks。

在使用Hooks之前我们必须知道的几件事:

  • Hooks的使用完全根据我们的需要进行选择用还是不用。
  • Hooks是完全向下兼容。
  • Hooks现在完全可用。
  • Hooks的出现并没有改变我们之前对react的理解。
  • Hooks的出现并没有移除classes的计划。

那么Hooks出现的动机是什么:

  • 很难重用组件之间有状态的逻辑
  • 复杂的组件变得难以理解

2. State Hook(useState)

先来介绍第一个useState。可以在函数组件(function component)使用它。它在的作用是添加本地状态到当前组件。React会一直维持在这个state。useState将会有两个返回值:当前的state和一个更新state的方法
调用useState时传入的参数表示着initial State。我们可以多次调用useState在同一个组件中,表示创建多个状态。

import React, { useState } from 'react'

    const HookDemo = ({}) => {
    const [count, setCount] = useState(0)
    const [number, setNumber] = useState(2)
    const [todos, setTodos] = useState([{text: 'huzhiwei'}])

    const setHandle = () => {
      todos[0].text = 'jp'
      setTodos(Object.assign({}, todos))
    }
    return (
      count: {count}
      number: {number}
      todos: {todos[0].text}
      <button onClick={()=>setCount(count+1)}>count handler</button>
      <button onClick={()=>setNumber(number+1)}>number handler</button>
      <button onClick={()=>setHandle()}>todos handler</button>
    )
}

3. Effect Hook (useEffect)

我们可能执行过请求获取数据,监听,或者手动改变DOM。我们可以称为这些为副作用。因为这些操作可能会影响到其他组件或者不能在渲染组件中完成。

Effect Hook给函数组件增加了处理副作用的能力。可以理解它的能力和componentDidMount、componentDidUpdate、componentWillUnmount类似。useEffect将这些统一为一个单独的API。
如下是React文档中的例子:

import React, { useState, useEffect } from 'react'

function Example() {
    const [count, setCount] = useState(0)
    
    useEffect(() => {
        document.title = 'good body'
    })
    
    return (
        <div>
            <span>{ count }</span>
            <button onClick={()=>setCount(count + 1)}>click me</button>
        </div>
    )
}   

当我们调用useEffect时,就是告诉React在刷新对DOM的更改后执行effect函数。React会在每次渲染后执行effect函数。

effect还可以选择性地指定如何通过返回函数在它们之后“清理”。例如,该组件使用一个效果来订阅朋友的在线状态,并通过取消订阅来清理:

import React, { useState, useEffect } from 'react'

function FriendStatus(props) {
    const [isOnline, setIsOnline] = useState(null)
    
    function handleStatusChange(status) {
        setIsOnline(status.isOnline)
    }
    
    useEffect(() => {
        ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange)
        return () => {
            ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange)
        }
    })
    
    if(isOnline === null) {
        return 'Loading...'
    }
    return isOnline ? 'Online' : 'Offline'
}

在这个例子中,当组件unmount的时候React将会通过ChatAPI取消订阅。在后续的render又将会重新执行这些effect。

Tip: 我们在组件中也可以像useState一样多次调用。

effect在第一次组件挂载和后面组件更新时都会执行,这样就会导致一些非必要的effect重复执行。如果我们想通过对比前后数据是否发生改变来判断是否触发effect,我们可以通过传入数组作为第二个参数:

useEffect(() => {
  document.title = `You clicked ${count} times`;
}, [count]); // 仅在 count 更改时更新
如果你想要执行只运行一次的 effect(仅在组件挂载和卸载时执行),你可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不算是一种特殊情况 —— 依然遵循输入数组的工作方式。

4. 创建自定义Hooks

有些场景下,我们想复用一些关于状态的逻辑。通常会有两种常用的解决方案:高阶组件render Props。自定义Hooks也可以做这些,而且不需要增加更多的组件。

这里有个例子使用useStateuseEffect去监听好友是否在线状态。我们想重用这个监听逻辑在不同的组件中该怎么做呢?

首先抽象出这个逻辑到一个自定义组件中,叫做useFriendStatus:

import React, {useState, useEffect} from 'react'

function useFriendStatus(frientID) {
    const [isOnline, setIsOnline] = useState(null)
    
    function handleStatusChange(status) {
        setIsOnline(status.isOnline)
    }
    
    useEffect(() => {
        ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange)
        return () => {
            ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange)
        }
    })
    
    return isOnline
}

如上,传入friendID作为参数,然后返回是否在线。现在我们看其他组件中如何使用:

function FriendStatus(props) {
    const isOnline = useFriendStatus(props.friend.id)
    if(isOnline === null) {
        return 'Loading...'
    }
    return isOnline ? 'online' : 'offline'
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

这些组件的状态是完全独立的。钩子是重用有状态逻辑的一种方法,而不是状态本身。事实上,每个对钩子的调用都有一个完全独立的状态——所以您甚至可以在一个组件中两次使用相同的自定义钩子。

5. Hooks规则

  • Hooks只在顶层调用,不要在循环,条件判断或者嵌套函数中调用钩子。
  • 只在React的函数组件(function Component)中调用Hooks。
  • 对于自定义Hooks,我们使用use开头命名。
  • eslint-plugin-react-hooks该插件可以规范hooks写法。
  • React 靠的是 Hook 调用的顺序来对应state和useState。

fsrookie
2.9k 声望256 粉丝

目前很多文章都是摘抄记录其他教程。见谅。